import 'package:family_expense/features/bank_card/models/bank_card.dart';
import 'package:family_expense/features/bank_card/services/bank_card_service.dart';
import 'package:family_expense/features/bank_card/widgets/bank_card_shared.dart';
import 'package:family_expense/features/bank_card/widgets/bank_shared.dart';
import 'package:family_expense/features/income_expense/constants/income_expense_type.dart';
import 'package:family_expense/features/income_expense/constants/payment_type.dart';
import 'package:family_expense/features/income_expense/models/income_expense_detail.dart';
import 'package:family_expense/features/income_expense/services/income_expense_detail_service.dart';
import 'package:family_expense/features/income_expense/widgets/income_expense_detail_shared.dart';
import 'package:family_expense/features/income_expense_detail_type/services/income_expense_detail_type_loader.dart';
import 'package:family_expense/main_view.dart';
import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:my_flutter_baidu_map/my_flutter_baidu_map.dart';
import 'package:my_flutter_core/my_flutter_core.dart';

class EditIncomeExpenseDetail extends StatefulWidget {
  const EditIncomeExpenseDetail({super.key, this.incomeExpenseDetail});

  final IncomeExpenseDetail? incomeExpenseDetail;

  @override
  State<EditIncomeExpenseDetail> createState() =>
      _EditIncomeExpenseDetailState();
}

class _EditIncomeExpenseDetailState extends State<EditIncomeExpenseDetail> {
  // For form validation.
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  // For display bank cards selection.
  late final BankCardService _bankCardService = BankCardService();

  // For any income/expense interaction with back end service.
  late final IncomeExpenseDetailService _incomeExpenseDetailService =
      IncomeExpenseDetailService();

  // The bank cards for selection.
  late final List<BankCard>? _bankCards = _bankCardService.currentBankCards;

  // Indicates current action is adding new data or updating data.
  // If [id] does not exist that means is adding new data.
  late final bool _addNew = widget.incomeExpenseDetail?.id == null;

  // For locating user position.
  late final MapLocationService _mapLocationService = MapLocationService(
    locationChanged: _onLcationChanged,
  );

  bool _isLocating = false;
  bool _inProgress = false;

  // Current income/expense data.
  IncomeExpenseDetail? _incomeExpenseDetail;

  double? _expressionResult;

  @override
  void initState() {
    super.initState();

    _createIncomeExpenseDetail();
    _initLocation();
  }

  @override
  void dispose() {
    _mapLocationService.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvokedWithResult: _onPopInvokedWithResult,
      child: Scaffold(
        appBar: AppBar(title: Text(_addNew ? '添加' : '修改')),
        body: _buildForm(),
      ),
    );
  }

  /// Builds the input form.
  Widget _buildForm() {
    if (_incomeExpenseDetail == null) {
      return const MyPageTopCenterLoadingIndicator();
    }

    // DO NOT PUT [Form] in [ListView]. Uses [SingleChildScrollView] instead.
    // Textfield in gallery gets reset if scrolled out of view.
    // https://github.com/flutter/flutter/issues/11500
    return Form(
      key: _formKey,
      child: SingleChildScrollView(
        padding: const EdgeInsets.all(10.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            ListTile(
              leading: Container(
                padding: const EdgeInsets.all(5.0),
                child: Icon(
                  Icons.location_on,
                  color:
                      _incomeExpenseDetail?.formattedAddress.isNullOrEmpty ==
                          true
                      ? const Color.fromARGB(255, 182, 185, 184)
                      : const Color(0xFF00C161),
                ),
              ),
              title: Text(
                _getLocationTip(),
                style: const TextStyle(fontSize: 14.0),
              ),
              trailing: const ListTileValueTrailing(),
              onTap: _gotoMapLocationSelector,
            ),
            const Divider(),
            SimpleDetailListTile(
              title: const Text('收支日期'),
              value: Text(
                _incomeExpenseDetail!.incomeExpenseDate.formatDate(
                  DatePatterns.longDate,
                ),
              ),
              onTap: () async {
                final selectedDate = await showMyDatePicker(
                  context,
                  initialDate: _incomeExpenseDetail!.incomeExpenseDate,
                );

                if (selectedDate != null && mounted) {
                  setState(
                    () =>
                        _incomeExpenseDetail!.incomeExpenseDate = selectedDate,
                  );
                }
              },
            ),
            SimpleDetailListTile(
              title: const Text('支出/收入'),
              value: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  IncomeExpenseDetailShared.getTypeIcon(
                    _incomeExpenseDetail!.type,
                  )!,
                  const SizedBox(width: 10.0),
                  Text(
                    IncomeExpenseType.getCategories()[_incomeExpenseDetail!
                        .type!]!,
                  ),
                ],
              ),
              onTap: () => _showIncomeExpenseTypeModalBottomSheet(),
            ),
            SimpleDetailListTile(
              title: const Text('收支类别'),
              value: Text(
                IncomeExpenseDetailShared.getIncomeOrExpenseTypeTitle(
                  _incomeExpenseDetail!,
                ),
              ),
              onTap: () =>
                  _incomeExpenseDetail!.type == IncomeExpenseType.income
                  ? _showIncomeTypeModalBottomSheet()
                  : _showExpenseTypeModalBottomSheet(),
            ),
            SimpleDetailListTile(
              title: const Text('收支方式'),
              value: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  IncomeExpenseDetailShared.getTypeIcon(
                        _incomeExpenseDetail!.paymentType,
                      ) ??
                      Container(),
                  const SizedBox(width: 10.0),
                  Text(
                    PaymentType.getCategories()[_incomeExpenseDetail!
                            .paymentType!] ??
                        '',
                  ),
                ],
              ),
              onTap: () {
                _showPaymentTypeModalBottomSheet();
              },
            ),
            SimpleDetailListTile(
              title: const Text('选择卡片'),
              value: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  if (_incomeExpenseDetail!.bankCard?.id != null)
                    BankLogo(_incomeExpenseDetail!.bankCard!.bank!),
                  const SizedBox(width: 10.0),
                  BankCardShared.buildBankCardName(
                    _incomeExpenseDetail!.bankCard,
                  ),
                ],
              ),
              onTap: () {
                BankCardShared.showBankCardModalBottomSheet(
                  context,
                  BankCardShared.getRelatedBankCards(
                    _bankCards!,
                    _incomeExpenseDetail!.paymentType,
                  ),
                  (bankCard) {
                    if (mounted) {
                      // Triggers the build.
                      setState(() => _incomeExpenseDetail!.bankCard = bankCard);
                    }

                    // Pops the modal.
                    NavigatorHelper.pop(context);
                  },
                  title: '选择卡片',
                  bankCard: _incomeExpenseDetail!.bankCard,
                );
              },
            ),
            const SizedBox(height: 10.0),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16.0),
              child: TextFormField(
                keyboardType: TextInputType.number,
                decoration: const InputDecoration(
                  hintText: '金额',
                  prefixIcon: Icon(Icons.attach_money),
                ),
                initialValue: '${_incomeExpenseDetail!.amount?.abs() ?? ''}',
                validator: (value) {
                  final validationResult = FormTextFieldValidator(
                    '金额',
                    value,
                  ).validateExpression();

                  _expressionResult = validationResult.expressionResult;

                  return validationResult.errorMessage;
                },
                onSaved: (value) =>
                    _incomeExpenseDetail!.amount = _expressionResult,
              ),
            ),
            _buildDivider(),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16.0),
              child: TextFormField(
                keyboardType: TextInputType.text,
                decoration: const InputDecoration(
                  hintText: '详情',
                  prefixIcon: Icon(Icons.comment),
                ),
                initialValue: _incomeExpenseDetail!.details,
                validator: (value) => FormTextFieldValidator(
                  '详情',
                  value,
                  maxLength: IncomeExpenseDetail.detailsMaxLength,
                ).validate(),
                onSaved: (value) => _incomeExpenseDetail!.details = value,
              ),
            ),
            _buildDivider(),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16.0),
              child: TextFormField(
                keyboardType: TextInputType.multiline,
                maxLines:
                    null, // This makes the TextField expandable. The default value is 1.
                decoration: const InputDecoration(
                  hintText: '备注',
                  prefixIcon: Icon(Icons.note),
                ),
                validator: (value) => FormTextFieldValidator(
                  '备注',
                  value,
                  maxLength: IncomeExpenseDetail.remarkMaxLength,
                  isRequired: false,
                ).validate(),
                initialValue: _incomeExpenseDetail!.remark,
                onSaved: (value) => _incomeExpenseDetail!.remark = value,
              ),
            ),
            _buildDivider(),
            Container(
              margin: const EdgeInsets.only(top: 40.0),
              height: 44.0,
              child: PressStatusButton(
                onPressed: _trySave,
                inProgress: _inProgress,
                disabledChild: const Text(
                  '正在保存',
                  style: TextStyle(fontSize: 20.0),
                ),
                child: _addNew
                    ? _buildIconText(MdiIcons.plus, '添 加')
                    : _buildIconText(MdiIcons.pencil, '修 改'),
              ),
            ),
          ],
        ),
      ),
    );
  }

  /// Builds divider between list tile.
  Widget _buildDivider() {
    return const SizedBox(height: 20.0);
  }

  /// Builds a Text with an Icon on its left.
  Widget _buildIconText(IconData icon, String titile) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(
          icon,
          color: calculateContrastingColor(
            Theme.of(context).appBarTheme.backgroundColor!,
          ),
        ),
        const SizedBox(width: 20.0),
        Text(titile, style: const TextStyle(fontSize: 20.0)),
      ],
    );
  }

  /// Listens on location change event and updates location info.
  void _onLcationChanged(MapLocation location) async {
    _setLocation(location);

    if (mounted) {
      setState(() {
        _isLocating = false;
      });
    }
  }

  /// Goes to map location selector.
  Future<void> _gotoMapLocationSelector() async {
    final location = await NavigatorHelper.push<MapLocation>(
      context,
      MapLocationSelector(
        MapLocation(
          latitude: _incomeExpenseDetail!.latitude,
          longitude: _incomeExpenseDetail!.longitude,
          province: _incomeExpenseDetail!.province,
          city: _incomeExpenseDetail!.city,
          address: _incomeExpenseDetail!.address,
          formattedAddress: _incomeExpenseDetail!.formattedAddress,
        ),
      ),
    );

    if (location != null) {
      if (mounted) {
        setState(() {
          _setLocation(location);
        });
      }
    }
  }

  void _setLocation(MapLocation? location) {
    _incomeExpenseDetail!.latitude = location?.latitude;
    _incomeExpenseDetail!.longitude = location?.longitude;
    _incomeExpenseDetail!.address = location?.address;
    _incomeExpenseDetail!.formattedAddress = location?.formattedAddress;
    _incomeExpenseDetail!.province = location?.province;
    _incomeExpenseDetail!.city = location?.city;
    _incomeExpenseDetail!.district = location?.district;
  }

  String _getLocationTip() {
    if (_incomeExpenseDetail?.formattedAddress != null) {
      return _incomeExpenseDetail!.formattedAddress!;
    } else {
      return _addNew ? '正在定位 ...' : '点击修改位置';
    }
  }

  /// Creates new instance of [IncomeExpenseDetail] based on current widget argument.
  void _createIncomeExpenseDetail() {
    if (widget.incomeExpenseDetail == null) {
      // If user comes from [HomeView] or [IncomeExpenseSearchResult] then a new instance will be created.
      final detailTypes = IncomeExpenseDetailTypeLoader.getDetailTypes(
        IncomeExpenseType.expense,
      );

      _incomeExpenseDetail = IncomeExpenseDetail(
        detailType: detailTypes.isNotEmpty ? detailTypes.first.name : null,
        incomeExpenseDate: getCurrentDateTime(),
      );

      _incomeExpenseDetail!.bankCard = _getRelatedDefaultBankCard();
    } else {
      // If user comes from [IncomeExpenseDetailView] or [CreditCardBillView] the provided argument already contains all necessary data.
      // In order to void updating original data we need to clone it.
      _incomeExpenseDetail = widget.incomeExpenseDetail!.clone();
    }
  }

  Future<void> _initLocation() async {
    if (_addNew ||
        _incomeExpenseDetail?.latitude == null ||
        _incomeExpenseDetail?.longitude == null) {
      _mapLocationService.startLocation();
      _isLocating = true;
    }
  }

  /// Shows a pick modal for income or expense selection.
  void _showIncomeExpenseTypeModalBottomSheet() {
    IncomeExpenseDetailShared.showIncomeExpenseTypeModalBottomSheet(context, (
      type,
    ) {
      final detailTypes = IncomeExpenseDetailTypeLoader.getDetailTypes(type);

      if (mounted) {
        // Triggers build.
        setState(() {
          // When the type changes we need to reset previous data.
          _incomeExpenseDetail!.type = type;

          // Sets the default [detailType] for income or expense.
          _incomeExpenseDetail!.detailType = detailTypes.isNotEmpty
              ? detailTypes.first.name
              : null;
        });
      }

      // Closes modal.
      NavigatorHelper.pop(context);
    }, currentType: _incomeExpenseDetail!.type);
  }

  /// Shows a pick modal for income type selection.
  void _showIncomeTypeModalBottomSheet() {
    IncomeExpenseDetailShared.showIncomeDetailTypeModalBottomSheet(context, (
      type,
    ) {
      if (mounted) {
        // Triggers build.
        setState(() {
          _incomeExpenseDetail!.detailType = type;

          // If the type is refund related then we can set the default payment.
          if (type!.toLowerCase().contains(GeneralConstant.refundKeyword)) {
            if (type.toLowerCase().contains(
              GeneralConstant.creditCardKeyword,
            )) {
              _incomeExpenseDetail!.paymentType = PaymentType.creditCard;
              _incomeExpenseDetail!.bankCard = _getRelatedDefaultBankCard();
            } else {
              // The bank card is NOT mandatory so we do not set the default bank card.
              _incomeExpenseDetail!.paymentType = PaymentType.cash;
            }
          }
        });
      }

      // Closes modal.
      NavigatorHelper.pop(context);
    }, currentType: _incomeExpenseDetail!.detailType);
  }

  /// Shows a pick modal for expense type selection.
  void _showExpenseTypeModalBottomSheet() {
    IncomeExpenseDetailShared.showExpenseDetailTypeModalBottomSheet(context, (
      type,
    ) {
      if (mounted) {
        // Triggers build.
        setState(() => _incomeExpenseDetail!.detailType = type);
      }

      // Closes modal.
      NavigatorHelper.pop(context);
    }, currentType: _incomeExpenseDetail!.detailType);
  }

  /// Shows a pick modal for payment type selection.
  void _showPaymentTypeModalBottomSheet() {
    IncomeExpenseDetailShared.showPaymentTypeModalBottomSheet(context, (type) {
      if (mounted) {
        // Triggers build.
        setState(() {
          _incomeExpenseDetail!.paymentType = type;
          _incomeExpenseDetail!.bankCard = _getRelatedDefaultBankCard();
        });
      }

      // Closes modal.
      NavigatorHelper.pop(context);
    }, currentType: _incomeExpenseDetail!.paymentType);
  }

  /// Gets related default bank card based on current payment type.
  BankCard? _getRelatedDefaultBankCard() {
    final relatedBankCards = BankCardShared.getRelatedBankCards(
      _bankCards!,
      _incomeExpenseDetail!.paymentType,
    );

    if (_incomeExpenseDetail!.paymentType == PaymentType.creditCard) {
      // Gets the default bank card if related bank card exists.
      return relatedBankCards.isNotEmpty ? relatedBankCards.first : null;
    } else {
      // At this moment we just do not set the default debit bank card.
      return null;
    }
  }

  /// The [Form] only checks on input type fields.
  /// We have to validate the form selection manually.
  bool _validateFormSelection() {
    if (_incomeExpenseDetail!.incomeExpenseDate == null) {
      showToast('请选择收支日期');
      return false;
    }

    if (_incomeExpenseDetail!.type.isNullOrEmpty) {
      showToast('请选择支出/收入');
      return false;
    }

    if (_incomeExpenseDetail!.detailType.isNullOrEmpty) {
      showToast('请选择收支类型');
      return false;
    }

    if (_incomeExpenseDetail!.paymentType.isNullOrEmpty) {
      showToast('请选择收支方式');
      return false;
    }

    if (_incomeExpenseDetail!.paymentType == PaymentType.creditCard &&
        _incomeExpenseDetail!.bankCard == null) {
      showToast('请选择信用卡');
      return false;
    }

    return true;
  }

  /// Saves the new [IncomeExpenseDetail] to server and pops.
  Future<void> _trySave() async {
    // Validate returns true if the form is valid, or false otherwise.
    if (!_formKey.currentState!.validate()) {
      showToast('请正确填写各项信息');
      return;
    }

    if (!_validateFormSelection()) {
      return;
    }

    // Saves current form.
    _formKey.currentState!.save();

    if (_isLocating) {
      String msg;

      if (_incomeExpenseDetail!.latitude == null ||
          _incomeExpenseDetail!.longitude == null) {
        msg = '定位还未完成，是否继续保存？';
      } else {
        msg = '重新定位还未完成，是否继续保存？';
      }

      showConfirmDialog(context, msg, onConfirm: _save);
    } else {
      await _save();
    }
  }

  /// Saves the new [IncomeExpenseDetail] to server and pops.
  Future<void> _save() async {
    if (mounted) {
      setState(() => _inProgress = true);
    }

    if (_addNew) {
      // Saves the new income/expense and pops if successful.
      final success = await _incomeExpenseDetailService.add(
        _incomeExpenseDetail!,
      );

      if (success && mounted) {
        // Pops all pages and returns to the main view.
        // The route is [MainView] -> [EditIncomeExpense] -> [MainView].
        // [MainView] is the last page of the app. That's why need to pop all pages.
        NavigatorHelper.pushAndRemoveUntil(
          context,
          const MainView(),
          (route) => false,
        );
      }
    } else {
      // Updates existing income/expense and pops if successful.
      final success = await _incomeExpenseDetailService.update(
        _incomeExpenseDetail!,
      );

      if (success && mounted) {
        // Pops current page and returns to the detail view.
        // Pop method will not trigger WillPopScope.
        NavigatorHelper.pop(context, true);
      }
    }

    if (mounted) {
      setState(() => _inProgress = false);
    }
  }

  /// Checks the data before popping.
  void _onPopInvokedWithResult(bool didPop, dynamic result) {
    if (didPop) {
      return;
    }

    // If we check the if the data identical that means we have to call [_formKey.currentState.save()] first.
    // Then it would call validation and might failed. That's why we show the confirm directly.
    showConfirmDialog(
      context,
      '放弃保存？',
      onConfirm: () {
        // If user pops current view directly, that means nothing updated.
        NavigatorHelper.pop(context, false);
      },
    );
  }
}
